#include "../include/nfs.h"

extern struct nfs_super nfs_super;
extern struct custom_options nfs_options;

/**
 * @brief 获取文件名
 *
 * @param path
 * @return char*
 */
char *nfs_get_fname(const char *path)
{
    char ch = '/';
    // strrchr() 返回最后一个 '/' 的位置
    char *q = strrchr(path, ch) + 1;
    return q;
}

/**
 * @brief 计算路径的层级
 * exm: /av/c/d/f
 * -> lvl = 4
 * @param path
 * @return int
 */
int nfs_calc_lvl(const char *path)
{
    // char* path_cpy = (char *)malloc(strlen(path));
    // strcpy(path_cpy, path);
    char *str = path;
    int lvl = 0;
    if (strcmp(path, "/") == 0)
    {
        return lvl;
    }
    while (*str != NULL)
    {
        if (*str == '/')
        {
            lvl++;
        }
        str++;
    }
    return lvl;
}

/**
 * @brief 驱动读
 *
 * @param offset
 * @param out_content
 * @param size
 * @return int
 */
int nfs_driver_read(int offset, char *buf, int size)
{
    // 从 offset 开始，读 size 个字节，存入 buf
    int down = offset - offset % 512;           // 对齐的下界
    int bias = offset - down;                   // offset 与下界的距离
    int up = (offset + size) / 512 * 512 + 512; // 对齐的上界
    ddriver_seek(nfs_super.fd, down, SEEK_SET);
    char *temp_buf = (char *)malloc(up - down);
    char *temp = temp_buf;
    for (int i = down; i < up; i += 512)
    {
        ddriver_read(nfs_super.fd, temp, 512);
        temp += 512;
    }
    memcpy(buf, temp_buf + bias, size);
    free(temp_buf);
    return NFS_ERROR_NONE;
}
/**
 * @brief 驱动写
 *
 * @param offset
 * @param in_content
 * @param size
 * @return int
 */
int nfs_driver_write(int offset, char *buf, int size)
{
    // 从 offset 开始，写 size 个字节
    int down = offset - offset % 512;
    int bias = offset - down;
    int up = (offset + size) / 512 * 512 + 512;
    char *temp_buf = (char *)malloc(up - down);
    nfs_driver_read(down, temp_buf, up - down); // 读出需要的磁盘块到内存
    memcpy(temp_buf + bias, buf, size);         // 在内存覆盖指定内容
    char *temp = temp_buf;
    ddriver_seek(nfs_super.fd, down, SEEK_SET);
    for (int i = down; i < up; i += 512) // 将读出的磁盘块再依次写回到内存
    {
        ddriver_write(nfs_super.fd, temp, 512);
        temp += 512;
    }
    free(temp_buf);
    return NFS_ERROR_NONE;
}

int nfs_mount(struct custom_options options)
{
    int ret = NFS_ERROR_NONE;
    boolean is_init = FALSE;

    int driver_fd = ddriver_open((char *)nfs_options.device); // 打开驱动
    if (driver_fd < 0)
    {
        return driver_fd;
    }
    
    nfs_super.fd = driver_fd;

    // 读取磁盘超级块
    struct nfs_super_d nfs_super_d;
    nfs_driver_read(0, (char *)&nfs_super_d, sizeof(struct nfs_super_d));

    // 幻数不匹配，为第一次挂载，需要初始化磁盘超级块数据结构
    if (nfs_super_d.magic != NFS_MAGIC)
    {
        nfs_super_d.inode_bitmap_blks = 1;
        nfs_super_d.inode_bitmap_offset = 1;
        nfs_super_d.data_bitmap_blks = 1;
        nfs_super_d.data_bitmap_offset = nfs_super_d.inode_bitmap_offset + nfs_super_d.inode_bitmap_blks; // 2

        is_init = TRUE;
    }

    // 将磁盘超级块载入内存超级块
    nfs_super.inode_bitmap_blks = nfs_super_d.inode_bitmap_blks;
    nfs_super.inode_bitmap_offset = nfs_super_d.inode_bitmap_offset;
    nfs_super.data_bitmap_blks = nfs_super_d.data_bitmap_blks;
    nfs_super.data_bitmap_offset = nfs_super_d.data_bitmap_offset;

    nfs_super.inode_blks = (MAX_INODE * sizeof(struct nfs_inode_d) + SZ_BLK - 1) / SZ_BLK; // 8192 * 40 / 1024 = 320 块
    nfs_super.inode_offset = nfs_super.data_bitmap_offset + nfs_super.data_bitmap_blks;
    nfs_super.data_blks =
        (SZ_DISK / SZ_BLK) - 1 - nfs_super_d.inode_bitmap_blks - nfs_super_d.data_bitmap_blks - nfs_super.inode_blks;
    // 4096 - 1 - 1 - 1 - 320 = 3773 块
    nfs_super.data_offset = nfs_super.inode_offset + nfs_super.inode_blks;

    nfs_super.inode_bitmap = (char *)malloc(nfs_super_d.inode_bitmap_blks * SZ_BLK);
    nfs_driver_read(nfs_super_d.inode_bitmap_offset * SZ_BLK, nfs_super.inode_bitmap,
                    nfs_super_d.inode_bitmap_blks * SZ_BLK);

    nfs_super.data_bitmap = (char *)malloc(nfs_super_d.data_bitmap_blks * SZ_BLK);
    nfs_driver_read(nfs_super_d.data_bitmap_offset * SZ_BLK, nfs_super.data_bitmap, nfs_super_d.data_bitmap_blks * SZ_BLK);

    nfs_super.root_dentry = new_dentry("/", NFS_DIR, 0);

    if (is_init)
    {
        // 如果是初始化的文件系统，还要初始化根目录
        struct nfs_inode *root_inode = nfs_alloc_inode(nfs_super.root_dentry, NFS_DIR);
    }
    else
    {
        // 否则读入磁盘中存储的 super_block_d 并与 root_dentry 关联
        get_inode_from_dentry(nfs_super.root_dentry);
    }

    return ret;
}

int nfs_umount()
{
    // 从根节点递归写
    write_back_recurse(nfs_super.root_dentry);

    // 写超级块
    struct nfs_super_d nfs_super_d;
    nfs_super_d.magic = NFS_MAGIC;
    nfs_super_d.inode_bitmap_blks = nfs_super.inode_bitmap_blks;
    nfs_super_d.inode_bitmap_offset = nfs_super.inode_bitmap_offset;
    nfs_super_d.data_bitmap_blks = nfs_super.data_bitmap_blks;
    nfs_super_d.data_bitmap_offset = nfs_super.data_bitmap_offset;
    nfs_driver_write(0, (char *)&nfs_super_d, sizeof(struct nfs_super_d));

    // 写索引节点位图和数据节点位图
    nfs_driver_write(nfs_super.inode_bitmap_offset * SZ_BLK, nfs_super.inode_bitmap, nfs_super.inode_bitmap_blks * SZ_BLK);
    nfs_driver_write(nfs_super.data_bitmap_offset * SZ_BLK, nfs_super.data_bitmap, nfs_super.data_bitmap_blks * SZ_BLK);

    free(nfs_super.inode_bitmap);
    free(nfs_super.data_bitmap);

    // 关闭驱动
    ddriver_close(nfs_super.fd);

    return NFS_ERROR_NONE;
}

void nfs_alloc_dblk(char **bpt, int *bno)
{
    // 开辟一个数据块，返回指针和编号
    *bno = -1;
    for (int byte = 0; byte < nfs_super.data_bitmap_blks * SZ_BLK; byte++)
    {
        for (int bit = 0; bit < 8; bit++)
        {
            if (((nfs_super.data_bitmap[byte] >> bit) & 1) == 0) // 为 0 表示表示未被使用
            {
                *bno = byte * 8 + bit;
                nfs_super.data_bitmap[byte] |= (1 << bit);
                break;
            }
        }
        if (*bno != -1)
            break;
    }
    if (*bno == -1)
    {
        return;
    }
    *bpt = (char *)malloc(SZ_BLK);
}

struct nfs_dentry *new_dentry(char *name, int type, int ino)
{
    // 根据文件类型和 inode 编号建立一个新的目录项
    struct nfs_dentry *newdentry = (struct nfs_dentry *)malloc(sizeof(struct nfs_dentry));
    memcpy(newdentry->name, name, MAX_NAME_LEN);
    newdentry->type = type;
    newdentry->ino = ino;
    newdentry->inode_pt = NULL;
    newdentry->nxt = NULL;
    newdentry->fa = NULL;
    return newdentry;
}

struct nfs_inode *nfs_alloc_inode(struct nfs_dentry *dentry, int type)
{
    // 创建一个新的 inode，并与dentry相关联
    int freeno = -1;
    for (int byte = 0; byte < nfs_super.inode_bitmap_blks * SZ_BLK; byte++)
    {
        for (int bit = 0; bit < 8; bit++)
        {
            if (((nfs_super.inode_bitmap[byte] >> bit) & 1) == 0)
            {
                freeno = byte * 8 + bit;
                nfs_super.inode_bitmap[byte] |= (1 << bit);
                break;
            }
        }
        if (freeno != -1)
            break;
    }
    if (freeno == -1)
    {
        return NULL;
    }

    struct nfs_inode *newinode = (struct nfs_inode *)malloc(sizeof(struct nfs_inode));
    newinode->ino = freeno;
    newinode->size = 0;
    newinode->type = type;
    newinode->dircnt = 0;
    newinode->dentry_pt = dentry;
    newinode->dentry_sons = NULL;
    for (int k = 0; k < NFS_DATA_PER_FILE; k++)
    {
        newinode->datablock[k] = -1;
        newinode->datablock_pt[k] = NULL;
    }
    dentry->inode_pt = newinode;
    dentry->ino = newinode->ino;
    assert(dentry->type == newinode->type);
    return newinode;
}

struct nfs_inode *get_inode_from_dentry(struct nfs_dentry *dentry)
{
    // 根据 dentry 读入 inode

    // 若 inode 是目录，则创建其目录项的 subdentry 内存结构并形成链表，同时父指针链接到 dentry
    struct nfs_inode *inode = (struct nfs_inode *)malloc(sizeof(struct nfs_inode));
    struct nfs_inode_d inode_d;

    nfs_driver_read((nfs_super.inode_offset + dentry->ino) * SZ_BLK, (char *)&inode_d, sizeof(struct nfs_inode_d));

    inode->ino = inode_d.ino;
    inode->size = inode_d.size;
    inode->type = inode_d.type;
    inode->dircnt = inode_d.dircnt;
    inode->dentry_pt = dentry;
    inode->dentry_sons = NULL;
    dentry->inode_pt = inode;

    for (int k = 0; k < NFS_DATA_PER_FILE; k++)
    {
        inode->datablock[k] = inode_d.datablock[k];
        if (inode_d.datablock[k] != -1) // 存在数据块
        {
            char *content = (char *)malloc(SZ_BLK);
            nfs_driver_read((nfs_super.data_offset + inode->datablock[k]) * SZ_BLK, content, SZ_BLK);
            inode->datablock_pt[k] = content;
        }
    }

    if (inode->type == NFS_DIR)
    {
        // 根据datablock内容读入目录项
        struct nfs_dentry_d subdentry_d;

        // 每块中包含的目录项数目
        int dirs_per_blk = SZ_BLK / sizeof(struct nfs_dentry_d), last_blkno = -1;
        for (int i = 0, offset = 0; i < inode->dircnt; i++, offset += sizeof(struct nfs_dentry_d))
        {
            // 读入第i个目录项到subdentry_d
            if (i / dirs_per_blk != last_blkno)
            {
                // 当前所在数据块与前一个不同，更新 offset 和 last_blkno
                offset = (nfs_super.data_offset + inode->datablock[i / dirs_per_blk]) * SZ_BLK;
                last_blkno = i / dirs_per_blk;
            }
            nfs_driver_read(offset, (char *)&subdentry_d, sizeof(struct nfs_dentry_d));

            // 建立对应内存目录项，头插法插入链表，设置父指针
            struct nfs_dentry *subdentry = new_dentry(subdentry_d.name, subdentry_d.type, subdentry_d.ino);
            subdentry->nxt = inode->dentry_sons;
            inode->dentry_sons = subdentry;
            subdentry->fa = dentry;
        }
    }
    return inode;
}

void write_back_recurse(struct nfs_dentry *dentry)
{
    // 从当前 dentry 递归写
    struct nfs_inode *inode = dentry->inode_pt;

    // 写inode
    struct nfs_inode_d inode_d;
    inode_d.ino = inode->ino;
    inode_d.size = inode->size;
    inode_d.type = inode->type;
    inode_d.dircnt = inode->dircnt;

    for (int k = 0; k < NFS_DATA_PER_FILE; k++)
    {
        inode_d.datablock[k] = inode->datablock[k];
    }

    nfs_driver_write((nfs_super.inode_offset + inode->ino) * SZ_BLK, (char *)&inode_d, sizeof(struct nfs_inode_d));

    // 写data
    if (dentry->type == NFS_DIR) // 目录类型
    {
        struct nfs_dentry_d dentry_d;
        struct nfs_dentry *pt = inode->dentry_sons;
        // 每个数据块能存储的目录项数目
        int dirs_per_blk = SZ_BLK / sizeof(struct nfs_dentry_d), last_blkno = -1;
        int cnt = 0;
        for (int offset = 0; pt; pt = pt->nxt, offset += sizeof(struct nfs_dentry_d), cnt++)
        {
            // 当前目录项 cnt 所在的数据块与 last_blkno 不同，更新 offset 和 last_blkno
            if (cnt / dirs_per_blk != last_blkno)
            {
                offset = (nfs_super.data_offset + inode->datablock[cnt / dirs_per_blk]) * SZ_BLK;
                last_blkno = cnt / dirs_per_blk;
            }
            memcpy(dentry_d.name, pt->name, MAX_NAME_LEN);
            dentry_d.type = pt->type;
            dentry_d.ino = pt->ino;
            nfs_driver_write(offset, (char *)&dentry_d, sizeof(struct nfs_dentry_d));
            // 递归写回子目录项
            write_back_recurse(pt);
        }
    }
    else
    { // 文件类型，直接写
        for (int k = 0; k < NFS_DATA_PER_FILE; k++)
            if (inode->datablock[k] != -1) // 当前数据块存储数据
            {
                nfs_driver_write((nfs_super.data_offset + inode->datablock[k]) * SZ_BLK, inode->datablock_pt[k], SZ_BLK);
            }
    }
}

struct nfs_dentry *nfs_get_dentry(struct nfs_inode *inode, int dir)
{
    // 返回当前目录下的第 dir 个子目录项
    struct nfs_dentry *dentry_cursor = inode->dentry_sons;
    int cnt = 0;
    while (dentry_cursor)
    {
        if (dir == cnt)
        {
            return dentry_cursor;
        }
        cnt++;
        dentry_cursor = dentry_cursor->nxt;
    }
    return NULL;
}

struct nfs_dentry *nfs_lookup(const char *path, boolean *is_find, boolean *is_root)
{
    // 路径解析，根据路径查找对应的目录项
    assert(path[0] == '/'); // 从根目录开始
    struct nfs_dentry *cur_dentry = nfs_super.root_dentry;
    struct nfs_dentry *ret = NULL;
    int totlvl = nfs_calc_lvl(path);
    int lvl = 0;
    char *fname = NULL;
    char *pathcpy = strdup(path);
    *is_root = FALSE;
    *is_find = FALSE;

    // 根目录直接返回根目录项
    if (totlvl == 0)
    {
        *is_root = TRUE;
        *is_find = TRUE;
        ret = nfs_super.root_dentry;
    }
    else
    {
        // 获取最外层文件夹名称
        fname = strtok(pathcpy, "/");
        while (fname)
        {
            lvl++;
            // 当前目录项 inode 为空，从磁盘读取
            if (cur_dentry->inode_pt == NULL)
            {
                get_inode_from_dentry(cur_dentry);
            }

            struct nfs_inode *inode = cur_dentry->inode_pt;

            // 当前 inode 对应文件类型，且还没查找到对应层数，说明路径错误
            if (inode->type == NFS_FILE && lvl < totlvl)
            {
                ret = inode->dentry_pt;
                break;
            }

            // 当前inode对应目录
            if (inode->type == NFS_DIR)
            {
                struct nfs_dentry *child = inode->dentry_sons;
                int found = 0;

                // 遍历该目录下所有目录项
                while (child)
                {
                    if (strcmp(child->name, fname) == 0)
                    {
                        found = TRUE;
                        cur_dentry = child;
                        break;
                    }
                    child = child->nxt;
                }

                if (!found)
                {
                    *is_find = FALSE;
                    ret = inode->dentry_pt;
                    break;
                }

                if (found && lvl == totlvl)
                {
                    *is_find = TRUE;
                    ret = cur_dentry;
                    break;
                }
            }

            // 获取下一层文件夹名称
            fname = strtok(NULL, "/");
        }
    }

    // 对应dentry的inode还没读进来，则从磁盘加载 inode
    if (ret->inode_pt == NULL)
    {
        get_inode_from_dentry(ret);
    }

    free(pathcpy);
    return ret;
}